/* 
 * File:   Question.cpp
 * Author: eduardo
 *
 * Created on 6 de diciembre de 2012, 17:53
 */

#include <vector>
#include <qt4/QtCore/qstring.h>

#include "Question.h"
#include "AgentDB.h"
#include "DocTerms.h"
#include "Lex.h"

Question::Question() {
    _j = _totalDocs = 0;
}

Question::~Question() {
    _terms.clear();
    _documents.clear();
    _docSem.clear();
}

string Question::similarQuestion(const string& document) {
    string newQ = "";
    // Dado un Documento
    // Obtener todos los terminos del documento "document"
    DocTerms* d = new DocTerms();
    d = analyze(document);
    // Obtener vector pregunta
    // Para cada termino
    // int peso = ftij * fidj;
    map<string, int>::iterator it = d->getBegin();
    AgentDB::getAgentePtr()->getTotalDocs(&_totalDocs);
    while (it != d->getEnd()) {
        int fdj = 0;
        AgentDB::getAgentePtr()->get_fdj(it->first, &fdj);
        double peso = fdj == 0 ? 0 : (it->second * (log((double) _totalDocs / fdj) / log(10)));
        QString peso_aux;
        peso_aux.setNum(peso);
        newQ.append(it->first + " " + peso_aux.toStdString() + " ");
        it++;
    }
    return newQ;
}

bool sortItemsList(pair<int, double> p1, pair<int, double> p2) {
    return p1.second > p2.second;
}

vector<InfoDoc>* Question::makeQuestion(const string& q, bool checked) {
    _question = q;
    takeTerms();
    vector<Rcons> *sol = new vector<Rcons > ();
    // Recuperamos documentos que contienen los terminos
    for (int i = 0; i < _j; i++) {
        // Recuperamos si el termino existe
        if (AgentDB::getAgentePtr()->existTerm(_terms.at(i).first)) {
            AgentDB::getAgentePtr()->getIdDocs(_terms.at(i).first, &_documents);
        }
    }
    // Recuperamos el total de docs
    AgentDB::getAgentePtr()->getTotalDocs(&_totalDocs);
    // Los recorremos asignando la semejanza
    if (_documents.size() > 0) {
        int weights[_terms.size()][_documents.size()];
        map<int, int> mO;
        for (int i = 0; i < _terms.size(); i++) {
            if (AgentDB::getAgentePtr()->existTerm(_terms.at(i).first)) {
                mO = AgentDB::getAgentePtr()->fillOccu(_terms.at(i).first);
                set<int>::iterator itS = _documents.begin();
                for (int j = 0; j < _documents.size(); j++, itS++) {
                    if (mO.find(*itS) != mO.end()) {
                        weights[i][j] = mO[*itS];
                    } else {
                        weights[i][j] = 0;
                    }
                }
            } else {
                for (int j = 0; j < _documents.size(); j++) {
                    weights[i][j] = 0;
                }
            }
        }

        // Aplicar sumatorios
        double sum = 0;
        double sumi = 0;
        double sumj = 0;

        // Modificacion jl, sumj. Calcular solo una vez
        double wpjs[_terms.size()];
        for (int j = 0; j < _terms.size(); j++) {
            wpjs[j] = (double) _terms.at(j).second;
            sumj += (wpjs[j] * wpjs[j]);
        }
        sumj = sqrt(sumj);

        double pesos = 0;
        // Por cada documento; sem(p, di)
        for (int d = 0; d < _documents.size(); d++) {
            // Terminos j
            for (int j = 0; j < _terms.size(); j++) {
                // fdj
                int fdj = 0;
                for (int i = 0; i < _documents.size(); i++) {
                    if (weights[j][i] > 0) ++fdj;
                }
                // Valores acumulados
                //Nota: (log(_totalDocs / fdj) / log(10))); solo es necesario calcularlo una vez
                if (weights[j][d] != 0) { // Si el termino i pertenece al documento j
                    double aux1 = (weights[j][d] * (log((double) _totalDocs / (double) fdj) / log(10.0)));
                    sumi += (aux1 * aux1);
                    sum += (aux1 * wpjs[j]);
                }
                if (checked) pesos += (weights[j][d] * wpjs[j]);
            }

            // Guardar semejanza
            // sumi vale 0 cuando todos los documentos contienen todos los terminos de la pregunta
            // en este caso el modelo vectorial no sabe identificar al mejor -> asigna 0% 
            set<int>::iterator IT = _documents.begin();
            for (int a = 0; a < d; a++) IT++;
            double vFinal = sumi == 0 ? 0 : (sum / (sqrt(sumi) * sumj));
            if (checked)
                _docSem.push_back(make_pair(*IT, pesos * vFinal));
            else
                _docSem.push_back(make_pair(*IT, vFinal));
            // Reseteo de variables inter-iteracion
            sum = sumi = pesos = 0;
            // Reseteo de variables inter-iteracion
            sum = sumi = 0;
        }

        // Los mostramos en orden de semejanza
        _docSem.sort(sortItemsList);
        list < pair<int, double > > ::iterator itSe = _docSem.begin();
        for (int cont = 1; itSe != _docSem.end(); itSe++, cont++) {
            string file = AgentDB::getAgentePtr()->getIdDocsPath(itSe->first); // Ruta del archivo
            string title = file; // Titulo del documento
            char* aux1 = new char[file.size() + 1];
            strcpy(aux1, file.c_str());
            char* aux2 = strtok(aux1, "/");
            while (aux2 != NULL) {
                title = aux2;
                aux2 = strtok(NULL, "/");
            }
            delete aux1;
            delete aux2;
            int prec = cont; // Orden de aparicion
            int id = itSe->first; // Identificador unico del documento
            int perc = (int) (itSe->second * 100); // Tanto porciento de semejanza
            Rcons rc = {file, title, prec, id, perc};
            sol->push_back(rc);
        }
        XML write;
        write.createXML(_terms, *sol);
    }
    return sol;
}

void Question::takeTerms() {
    char* str;
    char* question = new char[_question.size() + 1];
    strcpy(question, _question.c_str());
    str = strtok(question, " ");
    pair<string, float> term = make_pair("", 1);
    while (str != NULL) {
        if (isalpha(str[0])) {
            if (term.first == "") {
                term.first = str;
            } else {
                addTerms(term);
                term = make_pair(str, 1);
            }
        } else if (isdigit(str[0]) && term.first != "") {
            term.second = atof(str);
            addTerms(term);
            term = make_pair("", 1);
        }
        str = strtok(NULL, " ");
    }
    if (term.first != "") {
        addTerms(term);
    }
    delete[] question;
}

void Question::addTerms(pair<string, float> p) {
    _terms.push_back(p);
    _j++;
}
